<template>
  <div class="max-w-6xl mx-auto bg-white p-6 rounded-xl shadow-md">
    <h1 class="text-2xl font-bold text-gray-800 mb-6">Vue3 Canvas 虚拟表格</h1>

    <div class="mb-4 flex justify-between items-center">
      <div>
        <span class="text-gray-600">数据量:</span>
        <select
          v-model="rowCount"
          @change="handleRowCountChange"
          class="ml-2 border rounded px-2 py-1"
        >
          <option value="500">500行</option>
          <option value="1000" selected>1000行</option>
          <option value="5000">5000行</option>
          <option value="10000">10000行</option>
        </select>
      </div>
      <div class="text-sm text-gray-500">
        {{ status }}
      </div>
    </div>

    <div class="table-container h-[500px]">
      <!-- 表格头部 -->
      <div class="table-header h-10 px-4 flex items-center">
        <div class="w-[100px] font-medium">ID</div>
        <div class="w-[200px] font-medium">名称</div>
        <div class="w-[150px] font-medium">类别</div>
        <div class="w-[150px] font-medium">日期</div>
        <div class="flex-1 font-medium">描述</div>
      </div>

      <!-- Canvas 绘制区域 -->
      <div
        class="relative h-[calc(100%-40px)] overflow-auto"
        ref="tableContainer"
        @scroll="handleContainerScroll"
      >
        <canvas
          id="tableCanvas"
          class="w-full h-full"
          ref="tableCanvas"
          @mousemove="handleCanvasMouseMove"
          @click="handleCanvasClick"
        ></canvas>

        <!-- 垂直滚动条 -->
        <div
          class="absolute right-0 top-10 bottom-0 w-6 flex items-center justify-center bg-gray-50"
        >
          <div
            id="vScrollbar"
            class="scrollbar cursor-pointer"
            :style="{
              height: scrollbarHeight + 'px',
              transform: `translateY(${scrollbarPosition}px)`,
            }"
            @mousedown="startDragScrollbar"
          ></div>
        </div>
      </div>
    </div>

    <!-- 选中行信息 -->
    <div
      v-if="selectedRow"
      class="mt-4 p-3 bg-blue-50 rounded border border-blue-100"
    >
      <p class="text-sm font-medium text-blue-800">
        选中行: {{ selectedRow.id }} - {{ selectedRow.name }}
      </p>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, computed, watch } from 'vue';

// 表格配置
const rowHeight = 40; // 每行高度
const columns = [
  { key: 'id', width: 100 },
  { key: 'name', width: 200 },
  { key: 'category', width: 150 },
  { key: 'date', width: 150 },
  { key: 'description', width: 'flex' }, // 自适应剩余宽度
];

// 引用
const tableContainer = ref(null);
const tableCanvas = ref(null);

// 状态
const rowCount = ref(1000);
const data = ref([]);
const scrollTop = ref(0);
const status = ref('就绪 - 只渲染可视区域内容');
const selectedRow = ref(null);

// 计算属性
const visibleHeight = computed(() => {
  return tableContainer.value ? tableContainer.value.clientHeight : 0;
});

const visibleRowsCount = computed(() => {
  return Math.ceil(visibleHeight.value / rowHeight) + 2; // 多加2行用于缓冲
});

const startRowIndex = computed(() => {
  return Math.floor(scrollTop.value / rowHeight);
});

const endRowIndex = computed(() => {
  return Math.min(
    startRowIndex.value + visibleRowsCount.value,
    data.value.length - 1
  );
});

const scrollbarHeight = computed(() => {
  if (data.value.length === 0) return 0;
  const ratio = visibleHeight.value / (data.value.length * rowHeight);
  return Math.max(30, visibleHeight.value * ratio); // 最小高度30px
});

const scrollbarPosition = computed(() => {
  if (data.value.length === 0) return 0;
  const ratio =
    scrollTop.value / (data.value.length * rowHeight - visibleHeight.value);
  const maxPosition = visibleHeight.value - scrollbarHeight.value;
  return Math.min(maxPosition, ratio * maxPosition);
});

// 生成模拟数据
const generateData = (count) => {
  const categories = ['产品', '服务', '配件', '耗材', '其他'];
  const data = [];

  for (let i = 1; i <= count; i++) {
    data.push({
      id: i,
      name: `项目 ${i}`,
      category: categories[Math.floor(Math.random() * categories.length)],
      date: new Date(
        Date.now() - Math.floor(Math.random() * 365) * 24 * 60 * 60 * 1000
      ).toLocaleDateString(),
      description: `这是项目 ${i} 的详细描述信息，用于展示表格内容。`,
    });
  }

  return data;
};

// 初始化Canvas
const initCanvas = () => {
  const canvas = tableCanvas.value;
  if (!canvas) return;

  // 设置Canvas实际尺寸（考虑设备像素比）
  const updateCanvasSize = () => {
    if (!tableContainer.value) return;

    const { clientWidth, clientHeight } = tableContainer.value;
    const dpr = window.devicePixelRatio || 1;

    canvas.width = clientWidth * dpr;
    canvas.height = clientHeight * dpr;

    const ctx = canvas.getContext('2d');
    ctx.scale(dpr, dpr);

    canvas.style.width = `${clientWidth}px`;
    canvas.style.height = `${clientHeight}px`;

    renderTable();
  };

  updateCanvasSize();
  window.addEventListener('resize', updateCanvasSize);

  // 清理函数
  return () => {
    window.removeEventListener('resize', updateCanvasSize);
  };
};

// 渲染表格
const renderTable = () => {
  const canvas = tableCanvas.value;
  if (!canvas || !tableContainer.value) return;

  const ctx = canvas.getContext('2d');
  const { clientWidth } = tableContainer.value;

  // 清空画布
  ctx.clearRect(0, 0, clientWidth, visibleHeight.value);

  // 计算最后一列的宽度
  const fixedWidthSum = columns.reduce((sum, col) => {
    return sum + (col.width !== 'flex' ? col.width : 0);
  }, 0);
  const flexWidth = clientWidth - fixedWidthSum - 16; // 减去内边距

  // 渲染可见行
  const renderCount = endRowIndex.value - startRowIndex.value + 1;
  status.value = `渲染中 - 总数据: ${data.value.length}行, 可视区域: ${renderCount}行`;

  for (let i = startRowIndex.value; i <= endRowIndex.value; i++) {
    const row = data.value[i];
    if (!row) continue;

    // 计算行位置
    const y = i * rowHeight - scrollTop.value;

    // 绘制行背景
    ctx.fillStyle = i % 2 === 0 ? '#f8fafc' : '#ffffff';
    ctx.fillRect(0, y, clientWidth, rowHeight);

    // 绘制分隔线
    ctx.strokeStyle = '#e2e8f0';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(0, y + rowHeight);
    ctx.lineTo(clientWidth, y + rowHeight);
    ctx.stroke();

    // 绘制单元格
    let x = 8; // 左内边距
    columns.forEach((col) => {
      const width = col.width === 'flex' ? flexWidth : col.width;

      // 绘制单元格内容
      ctx.fillStyle = '#334155';
      ctx.font = '14px Inter, sans-serif';
      ctx.textBaseline = 'middle';
      ctx.fillText(row[col.key] || '', x, y + rowHeight / 2, width - 16); // 减去内边距

      x += width;
    });
  }

  status.value = `就绪 - 总数据: ${data.value.length}行, 可视区域: ${renderCount}行`;
};

// 事件处理
const handleContainerScroll = () => {
  if (tableContainer.value) {
    scrollTop.value = tableContainer.value.scrollTop;
    renderTable();
  }
};

const handleRowCountChange = () => {
  data.value = generateData(Number(rowCount.value));
  scrollTop.value = 0;
  if (tableContainer.value) {
    tableContainer.value.scrollTop = 0;
  }
  renderTable();
};

const handleCanvasMouseMove = (e) => {
  // 可以在这里实现鼠标悬停高亮效果
};

const handleCanvasClick = (e) => {
  if (!tableContainer.value) return;

  const rect = tableContainer.value.getBoundingClientRect();
  const clickY = e.clientY - rect.top;

  // 计算点击的行索引
  const rowIndex = startRowIndex.value + Math.floor(clickY / rowHeight);

  if (rowIndex >= 0 && rowIndex < data.value.length) {
    selectedRow.value = data.value[rowIndex];
  }
};

// 滚动条拖动
const startDragScrollbar = (e) => {
  e.preventDefault();

  const startY = e.clientY;
  const startScrollTop = scrollTop.value;
  const container = tableContainer.value;

  if (!container) return;

  const maxScrollTop = data.value.length * rowHeight - visibleHeight.value;
  const maxScrollbarY = visibleHeight.value - scrollbarHeight.value;

  const handleMouseMove = (e) => {
    const deltaY = e.clientY - startY;
    const scrollRatio = deltaY / maxScrollbarY;
    const newScrollTop = startScrollTop + scrollRatio * maxScrollTop;

    scrollTop.value = Math.max(0, Math.min(maxScrollTop, newScrollTop));
    container.scrollTop = scrollTop.value;
    renderTable();
  };

  const handleMouseUp = () => {
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };

  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);
};

// 初始化
onMounted(() => {
  data.value = generateData(Number(rowCount.value));
  const cleanup = initCanvas();

  // 监听滚动和数据变化
  watch(scrollTop, renderTable);
  watch(data, renderTable);

  // 初始渲染
  renderTable();

  // 清理
  return () => {
    if (cleanup) cleanup();
  };
});
</script>

<style scoped>
.table-container {
  @apply relative border border-gray-200 rounded-lg overflow-hidden;
}

.table-header {
  @apply sticky top-0 bg-[#f1f5f9] border-b border-gray-200 z-10;
}

.scrollbar {
  @apply appearance-none w-2 bg-gray-300 rounded-full hover:bg-gray-400 transition-colors;
}
</style>
